
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <link href='/css/styles.css' rel='stylesheet' type='text/css' />
    <link href='/images/favicon.png' rel='shortcut icon' />
    <script src='/js/jquery.min.1.4.js'></script>
    <script src='/js/app.js'></script>
    <meta content='width=device-width, minimum-scale=1.0, maximum-scale=1.0' name='viewport' />
    <title>﻿Redis Event Library – Redis中文资料站</title>
	<meta http-equiv="description" content="redis中文资料站，下载安装redis，查找redis常用命令（commands），选择适合的redis客户端方式，配置redis主从（master-slave），阅读redis官方文档，社区里了解更多redis信息，提交redis的bug。" />
  </head>
  <body class=''>
    <script src='/js/head.js'></script>
    <div class='text'>
      <article id='topic'>
        <h1>﻿Redis Event Library</h1>
        
        <p>Redis implements its own event library. The event library is implemented in <code>ae.c</code>.</p>
        
        <p>The best way to understand how the Redis event library works is to understand how Redis uses it.</p>
        
        <h2>Event Loop Initialization</h2>
        
        <p><code>initServer</code> function defined in <code>redis.c</code> initializes the numerous fields of the <code>redisServer</code> structure variable. One such field is the Redis event loop <code>el</code>:</p>
        
        <pre><code>aeEventLoop *el&#x000A;</code></pre>
        
        <p><code>initServer</code> initializes <code>server.el</code> field by calling <code>aeCreateEventLoop</code> defined in <code>ae.c</code>. The definition of <code>aeEventLoop</code> is below:</p>
        
        <pre><code>typedef struct aeEventLoop&#x000A;{&#x000A;    int maxfd;&#x000A;    long long timeEventNextId;&#x000A;    aeFileEvent events[AE_SETSIZE]; /* Registered events */&#x000A;    aeFiredEvent fired[AE_SETSIZE]; /* Fired events */&#x000A;    aeTimeEvent *timeEventHead;&#x000A;    int stop;&#x000A;    void *apidata; /* This is used for polling API specific data */&#x000A;    aeBeforeSleepProc *beforesleep;&#x000A;} aeEventLoop;&#x000A;</code></pre>
        
        <h2><code>aeCreateEventLoop</code></h2>
        
        <p><code>aeCreateEventLoop</code> first <code>malloc</code>s <code>aeEventLoop</code> structure then calls <code>ae_epoll.c:aeApiCreate</code>.</p>
        
        <p><code>aeApiCreate</code> <code>malloc</code>s <code>aeApiState</code> that has two fields - <code>epfd</code> that holds the <code>epoll</code> file descriptor returned by a call from <a href="http://man.cx/epoll_create%282%29"><code>epoll_create</code></a> and <code>events</code> that is of type <code>struct epoll_event</code> define by the Linux <code>epoll</code> library. The use of the <code>events</code> field will be  described later.</p>
        
        <p>Next is <code>ae.c:aeCreateTimeEvent</code>. But before that <code>initServer</code> call <code>anet.c:anetTcpServer</code> that creates and returns a <em>listening descriptor</em>. The descriptor listens on <em>port 6379</em> by default. The returned  <em>listening descriptor</em> is stored in <code>server.fd</code> field.</p>
        
        <h2><code>aeCreateTimeEvent</code></h2>
        
        <p><code>aeCreateTimeEvent</code> accepts the following as parameters:</p>
        
        <ul>
        <li><code>eventLoop</code>: This is <code>server.el</code> in <code>redis.c</code></li>
        <li>milliseconds: The number of milliseconds from the current time after which the timer expires.</li>
        <li><code>proc</code>: Function pointer. Stores the address of the function that has to be called after the timer expires.</li>
        <li><code>clientData</code>: Mostly <code>NULL</code>.</li>
        <li><code>finalizerProc</code>: Pointer to the function that has to be called before the timed event is removed from the list of timed events.</li>
        </ul>
        
        
        <p><code>initServer</code> calls <code>aeCreateTimeEvent</code> to add a timed event to <code>timeEventHead</code> field of <code>server.el</code>. <code>timeEventHead</code> is a pointer to a list of such timed events. The call to <code>aeCreateTimeEvent</code> from <code>redis.c:initServer</code> function is given below:</p>
        
        <pre><code>aeCreateTimeEvent(server.el /*eventLoop*/, 1 /*milliseconds*/, serverCron /*proc*/, NULL /*clientData*/, NULL /*finalizerProc*/);&#x000A;</code></pre>
        
        <p><code>redis.c:serverCron</code> performs many operations that helps keep Redis running properly.</p>
        
        <h2><code>aeCreateFileEvent</code></h2>
        
        <p>The essence of <code>aeCreateFileEvent</code> function is to execute <a href="http://man.cx/epoll_ctl"><code>epoll_ctl</code></a> system call which adds a watch for <code>EPOLLIN</code> event on the <em>listening descriptor</em> create by <code>anetTcpServer</code> and associate it with the <code>epoll</code> descriptor created by a call to <code>aeCreateEventLoop</code>.</p>
        
        <p>Following is an explanation of what precisely <code>aeCreateFileEvent</code> does when called from <code>redis.c:initServer</code>.</p>
        
        <p><code>initServer</code> passes the following arguments to <code>aeCreateFileEvent</code>:</p>
        
        <ul>
        <li><code>server.el</code>: The event loop created by <code>aeCreateEventLoop</code>. The <code>epoll</code> descriptor is got from <code>server.el</code>.</li>
        <li><code>server.fd</code>: The <em>listening descriptor</em> that also serves as an index to access the relevant file event structure from the <code>eventLoop-&gt;events</code> table and store extra information like the callback function.</li>
        <li><code>AE_READABLE</code>: Signifies that <code>server.fd</code> has to be watched for <code>EPOLLIN</code> event.</li>
        <li><code>acceptHandler</code>: The function that has to be executed when the event being watched for is ready. This function pointer is stored in <code>eventLoop-&gt;events[server.fd]-&gt;rfileProc</code>.</li>
        </ul>
        
        
        <p>This completes the initialization of Redis event loop.</p>
        
        <h2>Event Loop Processing</h2>
        
        <p><code>ae.c:aeMain</code> called from <code>redis.c:main</code> does the job of processing the event loop that is initialized in the previous phase.</p>
        
        <p><code>ae.c:aeMain</code> calls <code>ae.c:aeProcessEvents</code> in a while loop that processes pending time and file events.</p>
        
        <h2><code>aeProcessEvents</code></h2>
        
        <p><code>ae.c:aeProcessEvents</code> looks for the time event that will be pending in the smallest amount of time by calling <code>ae.c:aeSearchNearestTimer</code> on the event loop. In our case there is only one timer event in the event loop that was created by <code>ae.c:aeCreateTimeEvent</code>.</p>
        
        <p>Remember, that timer event created by <code>aeCreateTimeEvent</code> has by now probably elapsed because it had a expiry time of one millisecond. Since, the timer has already expired the seconds and microseconds fields of the <code>tvp</code> <code>timeval</code> structure variable is initialized to zero.</p>
        
        <p>The <code>tvp</code> structure variable along with the event loop variable is passed to <code>ae_epoll.c:aeApiPoll</code>.</p>
        
        <p><code>aeApiPoll</code> functions does a <a href="http://man.cx/epoll_wait"><code>epoll_wait</code></a> on the <code>epoll</code> descriptor and populates the <code>eventLoop-&gt;fired</code> table with the details:</p>
        
        <ul>
        <li><code>fd</code>: The descriptor that is now ready to do a read/write operation depending on the mask value.</li>
        <li><code>mask</code>: The read/write event that can now be performed on the corresponding descriptor.</li>
        </ul>
        
        
        <p><code>aeApiPoll</code> returns the number of such file events ready for operation. Now to put things in context, if any client has requested for a connection then <code>aeApiPoll</code> would have noticed it and populated the <code>eventLoop-&gt;fired</code> table with an entry of the descriptor being the <em>listening descriptor</em> and mask being <code>AE_READABLE</code>.</p>
        
        <p>Now, <code>aeProcessEvents</code> calls the <code>redis.c:acceptHandler</code> registered as the callback. <code>acceptHandler</code> executes <a href="http://man.cx/accept">accept</a> on the <em>listening descriptor</em> returning a <em>connected descriptor</em> with the client. <code>redis.c:createClient</code> adds a file event on the <em>connected descriptor</em> through a call to <code>ae.c:aeCreateFileEvent</code> like below:</p>
        
        <pre><code>if (aeCreateFileEvent(server.el, c-&gt;fd, AE_READABLE,&#x000A;    readQueryFromClient, c) == AE_ERR) {&#x000A;    freeClient(c);&#x000A;    return NULL;&#x000A;}&#x000A;</code></pre>
        
        <p><code>c</code> is the <code>redisClient</code> structure variable and <code>c-&gt;fd</code> is the connected descriptor.</p>
        
        <p>Next the <code>ae.c:aeProcessEvent</code> calls <code>ae.c:processTimeEvents</code></p>
        
        <h2><code>processTimeEvents</code></h2>
        
        <p><code>ae.processTimeEvents</code> iterates over list of time events starting at <code>eventLoop-&gt;timeEventHead</code>.</p>
        
        <p>For every timed event that has elapsed <code>processTimeEvents</code> calls the registered callback. In this case it calls the only timed event callback registered, that is, <code>redis.c:serverCron</code>. The callback returns the time in milliseconds after which the callback must be called again. This change is recorded via a call to <code>ae.c:aeAddMilliSeconds</code> and will be handled on the next iteration of <code>ae.c:aeMain</code> while loop.</p>
        
        <p>That's all.</p>
      </article>
    </div>
    <div class='text' id='comments'>
      <div id='disqus_thread'></div>
      <script type='text/javascript'>
        //<![CDATA[
          var disqus_shortname = 'rediscn';
          
          /* * * DON'T EDIT BELOW THIS LINE * * */
          (function() {
            var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
              dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
              (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
          })();
        //]]>
      </script>
      <a class='dsq-brlink' href='http://disqus.com'>
        Comments powered by
        <span class='logo-disqus'>
          Disqus
        </span>
      </a>
    </div>
    <script src='/js/foot.js'></script>
  </body>
</html>
